<?php
/**
 * dbfront
 * (c) 2010-2013 Stephen Adkins <spadkins@gmail.com>
 * This software began development in 2010.
 * It is based on code dating from 1999.
 *
 * License: GPL 2.0
 * The contents of this file may be used under the terms of
 * the GNU General Public License Version 2 or later (the "GPL").
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 * */

# Get rid of this error using the following PHPUnit comments.
#     PDOException: You cannot serialize or unserialize PDO instances
/**
 * @backupGlobals          disabled
 * @backupStaticAttributes disabled
 */

class APIWebServiceTest extends PHPUnit_Framework_TestCase {
    private static $ua, $fh, $http_user_agent, $client_connection_token, $username, $password, $email;

    protected static function _setUpBeforeClass ($class) {
        global $context, $options;

        self::$email    = 'scott9876@mailinator.com';
        self::$password = 'tiger';
        self::$username = 'scott';
        self::$http_user_agent = 'PHP/curl (phpunit) Internal Test Suite';
        self::$client_connection_token = '29426efc143b2d5511f46e38c358e92f0d313cf8d39bc9abe23547ad0caad6a8';

        # These variables are necessary for computing the client_connection_token
        $_SERVER['REMOTE_ADDR']     = '127.0.0.1';
        $_SERVER['HTTP_USER_AGENT'] = self::$http_user_agent;

        self::$ua = curl_init();

        $result = curl_setopt(self::$ua, CURLOPT_USERAGENT, self::$http_user_agent);
        $result = curl_setopt(self::$ua, CURLOPT_RETURNTRANSFER, true);

        $prefix = $options['prefix'];
        if ($prefix[0] !== '/') {
            $prefix = getcwd() . '/' . $prefix;
        }
        $result = curl_setopt(self::$ua, CURLOPT_COOKIE, "appdata=$prefix");

        self::$fh = fopen("out/$class.out", "w");
    }

    protected static function _tearDownAfterClass () {
        global $context, $options;
        fclose(self::$fh);
    }

    # request_content_type: auto, form, json
    protected function _callWebService ($http_method, $service, $request, &$response, $request_content_type='auto') {
        global $options;
        $url = $options['apps_urldir'] . '/sitefront/front-framework/api.php' . $service;

        fwrite(self::$fh, "##########################################################################\n");
        fwrite(self::$fh, "# METHOD:  $http_method\n");
        fwrite(self::$fh, "# SERVICE: $service\n");
        fwrite(self::$fh, "# URL:     $url\n");
        fwrite(self::$fh, "##########################################################################\n");
        fwrite(self::$fh, "REQUEST:\n");
        fwrite(self::$fh, json_encode($request,JSON_PRETTY_PRINT)."\n");
        #fwrite(self::$fh, print_r($request,true));

        #$success = curl_setopt(self::$ua, CURLOPT_VERBOSE, true);

        if ($http_method === 'GET' && isset($request) && count($request) > 0) {
            $url .= '?' . http_build_query($request);
        }
        $success = curl_setopt(self::$ua, CURLOPT_URL, $url);
        if (!$success) throw new \Exception('Web Service call prep failed (CURLOPT_URL)');

        if ($http_method === 'GET') {
            $success = curl_setopt(self::$ua, CURLOPT_HTTPGET, true);
            if (!$success) throw new \Exception('Web Service call prep failed (CURLOPT_HTTPGET)');
        }
        #elseif ($http_method === 'POST' && $request_content_type === 'form') {
        #    $success = curl_setopt(self::$ua, CURLOPT_POST, true);
        #    if (!$success) throw new \Exception('Web Service call prep failed (CURLOPT_POST)');
        #    $success = curl_setopt(self::$ua, CURLOPT_POSTFIELDS, $request);
        #    if (!$success) throw new \Exception('Web Service call prep failed (CURLOPT_POSTFIELDS)');
        #}
        else {
            $success = curl_setopt(self::$ua, CURLOPT_CUSTOMREQUEST, $http_method);
            if (!$success) throw new \Exception('Web Service call prep failed (CURLOPT_CUSTOMREQUEST)');
            if ($request_content_type === 'auto') $request_content_type = 'json';
            if ($request_content_type === 'form') {
                $success = curl_setopt(self::$ua, CURLOPT_POSTFIELDS, $request);
                if (!$success) throw new \Exception('Web Service call prep failed (CURLOPT_POSTFIELDS)');
            }
            else {
                $json = json_encode($request);
                $success = curl_setopt(self::$ua, CURLOPT_POSTFIELDS, $json);
                if (!$success) throw new \Exception('Web Service call prep failed (CURLOPT_POSTFIELDS)');
                $success = curl_setopt(self::$ua, CURLOPT_HTTPHEADER, array('Content-Type: application/json','Content-Length: ' . strlen($json)));
                if (!$success) throw new \Exception('Web Service call prep failed (CURLOPT_HTTPHEADER)');
            }
        }
        $success = curl_setopt(self::$ua, CURLOPT_HTTPHEADER, array('Accept: application/json, text/html, text/plain'));
        if (!$success) throw new \Exception('Web Service call prep failed (CURLOPT_HTTPHEADER)');
        $success = curl_setopt(self::$ua, CURLOPT_HEADER, true);
        if (!$success) throw new \Exception('Web Service call prep failed (CURLOPT_HEADER)');

        $response_text   = curl_exec(self::$ua);
        if (!$response_text) throw new \Exception("Web Service call failed ($http_method $url)");
        $response_info   = curl_getinfo(self::$ua);
        $http_code       = $response_info['http_code'];
        $header_size     = $response_info['header_size'];
        $response_header = trim(substr($response_text, 0, $header_size));
        $response_body   = substr($response_text, $header_size);
        $response        = json_decode($response_body, true);
        fwrite(self::$fh, "RESPONSE (INFO):\n");
        #fwrite(self::$fh, print_r($response_info,true));
        fwrite(self::$fh, json_encode($response_info,JSON_PRETTY_PRINT)."\n");
        fwrite(self::$fh, "RESPONSE (RAW): status=($http_code)\n");
        fwrite(self::$fh, $response_text."\n");
        fwrite(self::$fh, "RESPONSE (JSON-PARSED):\n");
        fwrite(self::$fh, json_encode($response,JSON_PRETTY_PRINT)."\n");
        return($http_code);
    }

    protected function assertDeeply ($expected, $target, $name='var', $do_count=true) {
        $expected_type = $this->gettype($expected);
        $target_type   = $this->gettype($target);
        $this->assertTrue(strncmp($expected_type, $target_type, 3)==0, "$name: type [$target_type]=[$expected_type]");
        if ($expected_type === 'assoc') {
            $expected_count = count($expected);
            $target_count   = count($target);
            if ($do_count && isset($expected['_NOCOUNT_'])) $do_count = false;
            if ($do_count) $this->assertEquals($expected_count, $target_count, "$name: element count [$target_count]=[$expected_count]");
            foreach ($expected as $expected_key => $expected_value) {
                if ($expected_key !== '_NOCOUNT_') {
                    $this->assertTrue(array_key_exists($expected_key, $target), "${name}{".$expected_key."}: element exists");
                    $this->assertDeeply($expected_value, $target[$expected_key], "${name}{".$expected_key."}", $do_count);
                }
            }
        }
        elseif ($expected_type === 'array') {
            $expected_count = count($expected);
            $target_count   = count($target);
            if ($do_count) $this->assertEquals($expected_count, $target_count, "$name: element count [$target_count]=[$expected_count]");
            foreach ($expected as $expected_key => $expected_value) {
                if ($expected_value === '_NOCOUNT_') {
                    break;
                }
                else {
                    $this->assertTrue(array_key_exists($expected_key, $target), "${name}[$expected_key]: element exists");
                    $this->assertDeeply($expected_value, $target[$expected_key], "${name}[$expected_key]", $do_count);
                }
            }
        }
        elseif ($expected_type === 'NULL') {
            $this->assertTrue(true, "$name: value is null");
        }
        elseif ($expected_type === 'scalar-boolean' || $expected_type === 'scalar-integer' || $expected_type === 'scalar-double' || $expected_type === 'scalar-string') {
            if ($expected === '_ANY_') {
                # do nothing
            }
            elseif ($expected === '_DATE_') {
                $this->assertTrue(preg_match('/^\d\d\d\d-\d\d-\d\d$/', $target) === 1, "$name: is a date ($target)");
            }
            elseif ($expected === '_DATETIME_') {
                $this->assertTrue(preg_match('/^\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d$/', $target) === 1, "$name: is a datetime ($target)");
            }
            elseif ($expected === '_INTEGER_') {
                $this->assertTrue(is_numeric($target) && intval($target) == $target, "$name: is an integer ($target)");
            }
            elseif ($expected === '_FLOAT_') {
                $this->assertTrue(is_numeric($target), "$name: is a float ($target)");
            }
            elseif ($expected_type === 'scalar-boolean') {
                $this->assertTrue($expected === $target, "$name: value [".($target?'true':'false')."]=[".($expected?'true':'false')."]");
            }
            else {
                $this->assertEquals($expected, $target, "$name: value [$target]=[$expected]");
            }
        }
        else {    # "object", "resource", "unknown type"
            # assume equality
        }
    }

    protected function gettype ($var) {
        $type = gettype($var);
        if ($type === 'array' && $this->is_assoc($var)) {
            $type = 'assoc';
        }
        elseif ($type === 'boolean' || $type === 'integer' || $type === 'double' || $type === 'string') {
            $type = 'scalar-'.$type;
        }
        return($type);
    }

    protected function is_assoc (&$array) {
        if (!is_array($array))
            return 0;
        if (!array_key_exists(0, $array))
            return 1;
        $count = count($array);
        if (!array_key_exists($count-1, $array))
            return 1;
        return 0;
    }
}

